package com.codefish.codefishseckill.security;

import com.codefish.codefishseckill.controller.ResourceController;
import com.codefish.codefishseckill.entity.AuthUser;
import com.codefish.codefishseckill.entity.User;
import com.codefish.codefishseckill.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 自定义认证逻辑
 *
 * @author <a href='https://gitee.com/hzf2281'>codefish</a>
 * @version 1.0.0
 * @since 2022/09/14 下午 02:19
 */
@Component
public class CustomerAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    //loginType
    private static final String USERNAME_PASSWORD_TYPE = "0";
    private static final String EMAIL_CAPTCHA_TYPE = "1";

    @Autowired
    RedisTemplate<String, Object> redisTemplate;
    @Autowired
    UserService userService;

    @Autowired
    public CustomerAuthenticationFilter(AuthenticationSuccessHandler authSuccessHandler, AuthenticationFailureHandler authenticationFailureHandler) {
        super();
        //设置登录接口
        super.setFilterProcessesUrl("/form/login");
        super.setAuthenticationFailureHandler(authenticationFailureHandler);
        super.setAuthenticationSuccessHandler(authSuccessHandler);
    }

    @Override
    @Autowired
    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
        super.setAuthenticationManager(authenticationManager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (!"POST".equals(request.getMethod())) {
            //必须是post请求
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }

        String loginType = request.getParameter("loginType");
        if (USERNAME_PASSWORD_TYPE.equals(loginType)) {
            //账号 + 密码的登录方式，交由父类验证
            return super.attemptAuthentication(request, response);
        } else if (EMAIL_CAPTCHA_TYPE.equals(loginType)) {
            //邮箱 + 验证码的登录方式，由自身认证
            String email = request.getParameter("email");
            String inputCaptcha = request.getParameter("captcha");
            return getAuthentication(email, inputCaptcha);
        }

        //其它未定义认证方式,抛异常
        throw new AuthenticationServiceException("登录认证异常，不支持的登录方式");
    }


    private Authentication getAuthentication(String email, String inputCaptcha) throws AuthenticationException {
        //从redis中获取对于的验证码
        String realCaptcha = (String) redisTemplate.opsForValue().get(ResourceController.LOGIN_CAPTCHA_REDIS_PREFIX + email);

        if (StringUtils.isEmpty(realCaptcha) || !realCaptcha.equals(inputCaptcha)) {
            //验证码不匹配
            throw new AuthenticationServiceException("验证码错误");
        }
        //验证码匹配成功，删除redis中的验证码，使其失效
        redisTemplate.delete(ResourceController.LOGIN_CAPTCHA_REDIS_PREFIX + email);

        //匹配成功，需要从数据库加载用户基本信息
        User user = userService.getUserByEmail(email);
        if (user == null) {
            //用户尚未注册
            throw new AuthenticationServiceException("邮箱为" + email + "的用户尚未注册");
        }

        //包装用户信息并返回token
        AuthUser authUser = new AuthUser(user);
        return new UsernamePasswordAuthenticationToken(authUser, authUser.getAuthorities());
    }

}
